/**
 * 
 */
package cn.ad.server.web.servlet.v2.requesthandler;

import cn.ad.server.common.utils.BrowserUtils;
import cn.ad.server.common.utils.IPUtils;
import cn.ad.server.web.log.entity.AdTrackingEventLog;
import cn.ad.server.web.servlet.v2.parser.AdRequestParseException;
import cn.ad.server.web.servlet.v2.parser.ParameterParseExcption;
import cn.ad.server.web.servlet.v2.parser.TrackingRequestParser;
import cn.ad.server.web.servlet.v2.requests.*;
import cn.ad.server.web.spring.model.IAdserverRequest;
import cn.ad.server.web.spring.service.actions.ITrackingAction;
import cn.ad.server.web.spring.service.actions.RequestActionException;
import cn.ad.server.web.spring.service.actions.context.IPContext;
import cn.ad.server.domain.IPDetail;
import cn.ad.server.domain.enumration.ResponseStatusCode;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * 监测跟踪代码接口处理抽象类
 * 
 * @author Wu Chengjun
 */
public abstract class TrackingRequestHandler extends AdRequestHandler {

	private static Logger LOGGER = LoggerFactory.getLogger(TrackingRequestHandler.class);

	/**
	 * 接入渠道消息解析，针对特定渠道的请求消息解析
	 */
	protected Map<String, TrackingRequestParser> channelRequestParsers;

	/**
	 * 缺省的默认解析类
	 */
	protected TrackingRequestParser defaultParser;

	/**
	 * 跟踪請求處理Action
	 */
	private ITrackingAction trackAction;

	/**
	 * 
	 */
	public TrackingRequestHandler() {
		channelRequestParsers = new HashMap<>();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see cn.ad.server.web.servlet.v2.requesthandler.
	 * ServletRequestHandler#handle(javax.servlet.http.HttpServletRequest,
	 * javax.servlet.http.HttpServletResponse)
	 */
	@Override
	public void handle(HttpServletRequest request, HttpServletResponse response) throws RequestHandleException {
		TrackingRequest trackingReq = null;
		TrackingResponse trackingRes = null;
		IAdserverRequest adReq = null;
		try {
			String channel = getChannel(request);
			if (StringUtils.isAnyEmpty(channel)) {
				throw new RequestHandleException("无法获取请求中的接入渠道标识.");
			}
			TrackingRequestParser parser = getRequestParser(channel);
			if (parser == null) {
				parser = getDefaultParser();
			}

			if (parser == null) {
				throw new RequestHandleException("缺省的监测消息解析实例没有初始化.");
			}
			// 解析请求参数
			adReq = parser.parse(request);
			if (adReq == null) {
				throw new AdRequestParseException("AdRequest is null.");
			}

			if (adReq instanceof TrackingRequest) {
				trackingReq = (TrackingRequest) adReq;
				trackingReq.setChannelID(channel);
				// 验证请求对象
				validRequest(trackingReq);
				trackingRes = handleTracking(trackingReq);
//				if (trackingRes instanceof ClickResponse)
//					LOGGER.info("-----trackingRes : {}", trackingRes);
			} else {
				throw new RequestHandleException(
						"The Request of Tracking is not support. class:" + adReq.getClass().getName());
			}
			handleResponse(response, parser, trackingReq, trackingRes);
		} catch (ParameterParseExcption ex) {
			if (ex.getResult() != null) {
				trackingReq = (TrackingRequest) ex.getResult();
				trackingRes = new TrackingResponse(ResponseStatusCode.INVALID_REQ_PARAMETER.value());
			}
			LOGGER.warn("解析监测接口参数 [{} = {}]出现异常:" + ex.getMessage(), ex.getParamName(), ex.getParamValue(), ex);
		} catch (AdRequestParseException ex) {
			throw new RequestHandleException("请求消息解析出现异常.", ex);
		} catch (RequestParamValidFailedException e) {
			trackingRes = createExceptionResponse(trackingRes, ResponseStatusCode.INVALID_REQ_PARAMETER);
			throw new RequestHandleException("广告监测请求参数验证失败," + e.getMessage());
		} catch (Exception e) {
			// <<<<<<< HEAD
			// LOGGER.error("广告监测请求处理异常，系统内部错误:{}", e.getMessage(),
			// e.getCause());
			// e.printStackTrace();
			// =======
			LOGGER.error("广告监测请求处理异常，系统内部错误:{}", e.getMessage(), e);
			// >>>>>>> 27d09310a5dccf231e32016c999e39807ebde5ee
			trackingRes = createExceptionResponse(trackingRes, ResponseStatusCode.UNKNOWN_INNER_ERROR);
		} finally {
			logTrackingEvent(trackingReq, trackingRes);
		}
	}

	/**
	 * 生成异常响应
	 * 
	 * @param srcRes
	 *            原来的处理响应
	 * @param reStatusCode
	 *            异常状态
	 * @return
	 */
	private TrackingResponse createExceptionResponse(TrackingResponse srcRes, ResponseStatusCode reStatusCode) {
		TrackingResponse res = srcRes != null ? srcRes : (new TrackingResponse());
		res.setStatus(reStatusCode.value());
		return res;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see cn.ad.server.web.servlet.v2.requesthandler.
	 * AdRequestHandler#validRequest(cn.ad.server.web.spring.
	 * model.IAdserverRequest)
	 */
	@Override
	public void validRequest(IAdserverRequest request) throws RequestParamValidFailedException {
		super.validRequest(request);
		if (request instanceof TrackingRequest) {
			TrackingRequest req = (TrackingRequest) request;

			// TO-DO 增加当接入渠道类型为sts时，才允许sid为0
			// if(req.getSid() == 0){
			// throw new RequestParamValidFailedException("sid is zero.");
			// }

			if (StringUtils.isAnyEmpty(req.getGuid())) {
				throw new RequestParamValidFailedException("guid is null.");
			}

			if (req.getCid() == 0) {
				throw new RequestParamValidFailedException("cid is zero.");
			}
		} else {
			throw new IllegalArgumentException("The param must be " + TrackingRequest.class.getName() + ", but it's "
					+ request.getClass().getName());
		}
	}

	/**
	 * 记录监测请求处理日志
	 * 
	 * @param req
	 */
	public void logTrackingEvent(TrackingRequest req, TrackingResponse res) {
		if (req == null || res == null) {
			return;
		}

		AdTrackingEventLog event = new AdTrackingEventLog();

		event.setRequestTimestamp(req.getRequestTimestamp());
		event.setRequestIp(req.getIp());
		event.setServerId(IPUtils.getHostName());
		event.setPlatformId(req.getPlatform_id());
		event.setAdxType(req.getPlatform_id() == 0 ? AdTrackingEventLog.SYSTEM_PLATFORM_ID : req.getPlatform_id());
		event.setChannel_id(req.getChannelID());

		event.setGuid(req.getGuid());
		// event.setGuid(req.getGuid());
		event.setUa(req.getUa());
		event.setBrowser(BrowserUtils.getBrowser(req.getUa()));

		IPDetail ipDetail = IPContext.instance().getIPDetail(IPUtils.convertIPToLong(res.getIp()));
		if (ipDetail != null) {
			event.setCountryId(ipDetail.getCountryId());
			event.setStateId(ipDetail.getStateId());
			event.setCityId(ipDetail.getCityId());
			event.setAreaId(ipDetail.getDistrictId());
		}

		if (req instanceof ClickRequest) {
			event.setEventType(AdTrackingEventLog.EVENT_CLICK);
		} else if (req instanceof ImpressionRequest) {
			event.setEventType(AdTrackingEventLog.EVENT_IMPRESSION);
		} else if (req instanceof WinoticeRequest) {
			event.setEventType(AdTrackingEventLog.EVENT_BID_WIN);
		}

		// 设置事件异常状态
		event.setExceptionType(res.getStatus());
		if (res.getStatus() != 0) {
			event.setAdspaceId(req.getAid());
			event.setStrategyId(req.getSid());
			event.setCreativeId(req.getCid());
			event.setMaterialId(req.getMid());
			event.setExceptionMsg(res.getExceptionMsg());
		} else {
			event.setAdFormat(res.getAdFormat());
			event.setTemplateId(res.getTemplateId());
			event.setCampaignId(res.getCampaignId());

			event.setAdspaceId(res.getAdspaceId());
			event.setMuniqueType(String.valueOf(res.getMuniqueType()));
			event.setMuniqueId(res.getMuniqueId());
			event.setMobOps(String.valueOf(res.getMobOps()));
			event.setBrand(res.getBrand());
			event.setModel(res.getModel());
			event.setOs(String.valueOf(res.getOs()));
			event.setConnType(res.getConnType());
			event.setDeviceType(res.getDeviceType());
			event.setLatitude(String.valueOf(res.getLatitude()));
			event.setLongitude(String.valueOf(res.getLongitude()));
			event.setIp(res.getIp());
			event.setStrategyId(res.getStrategyId());
			event.setCreativeId(res.getCreativeId());
			event.setMaterialId(res.getMaterialId());

			event.setScreenWidth(res.getScreenWidth());
			event.setScreenHeight(res.getScreenHeight());

			event.setCreativeHeight(res.getCreativeHeight());
			event.setCreativeWidth(res.getCreativeWidth());

			event.setMac(res.getMac());
			event.setMac1(res.getMac1());
			event.setImei(res.getImei());
			event.setAnid(res.getAnid());
			event.setAaid(res.getAaid());
			event.setIdfa(res.getIdfa());
			event.setUdid(res.getUdid());
			event.setDuid(res.getDuid());

			event.setImeisha1(res.getImeisha1());
			event.setDpidsha1(res.getDpidsha1());
			event.setMacsha1(res.getMacsha1());
			event.setImeimd5(res.getImeimd5());
			event.setDpidmd5(res.getDpidmd5());
			event.setMacmd5(res.getMacmd5());
			event.setCustomId(res.getCustomId());
			event.setOpenUdid(res.getOpenUdid());

			event.setIncome(res.getIncome());
			event.setPayment(res.getPayment());
			event.setBchid(req.getCh());
		}
//		if (res instanceof ClickResponse)
//			LOGGER.info("--------event:{}", event);
		logAdserverEventLog(event);
	}

	/**
	 * 处理监测请求
	 * 
	 * @param impressionReq
	 * @param cp
	 * @return
	 * @throws RequestActionException
	 */
	public TrackingResponse handleTracking(TrackingRequest request) throws RequestActionException {
		return getTrackAction().handle(request);
	}

	/**
	 * 获取广告监听事件请求消息解析器
	 * 
	 * @param channelID
	 *            渠道标识
	 * @return
	 */
	public TrackingRequestParser getRequestParser(String channelID) {
		if (channelRequestParsers == null) {
			return null;
		}
		return channelRequestParsers.get(channelID);
	}

	/**
	 * @return the defaultParser
	 */
	public TrackingRequestParser getDefaultParser() {
		return defaultParser;
	}

	/**
	 * @param defaultParser
	 *            the defaultParser to set
	 */
	public void setDefaultParser(TrackingRequestParser defaultParser) {
		this.defaultParser = defaultParser;
	}

	/**
	 * @return the channelRequestParsers
	 */
	public Map<String, TrackingRequestParser> getChannelRequestParsers() {
		return channelRequestParsers;
	}

	/**
	 * @param channelRequestParsers
	 *            the channelRequestParsers to set
	 */
	public void setChannelRequestParsers(Map<String, TrackingRequestParser> channelRequestParsers) {
		this.channelRequestParsers = channelRequestParsers;
	}

	/**
	 * @return the trackAction
	 */
	public ITrackingAction getTrackAction() {
		return trackAction;
	}

	/**
	 * @param trackAction
	 *            the trackAction to set
	 */
	public void setTrackAction(ITrackingAction trackAction) {
		this.trackAction = trackAction;
	}
}
